﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Windows;
using System.Windows.Controls;
using Xceed.Silverlight.ListBox.PositioningFunction;
using System.Windows.Input;

namespace Xceed.Silverlight.ListBox
{
  public sealed class StackView : View, IOrientedView, IOrientedViewManager
  {
    #region Constructors

    public StackView()
    {
      this.PositioningFunction = new VerticalPositioning();
      this.PositioningContainerLayoutMode = ContainerLayoutMode.Vertical;
    }

    #endregion

    #region Orientation Property

    public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
      "Orientation",
      typeof( Orientation ),
      typeof( StackView ),
      new PropertyMetadata(
        Orientation.Vertical,
        new PropertyChangedCallback( StackView.OnOrientationChanged ) ) );

    public Orientation Orientation
    {
      get
      {
        return ( Orientation )this.GetValue( StackView.OrientationProperty );
      }
      set
      {
        this.SetValue( StackView.OrientationProperty, value );
      }
    }

    private static void OnOrientationChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      StackView view = sender as StackView;

      if( view == null )
        return;

      view.UpdatePositioningFunction();
    }

    #endregion

    #region Private Methods

    private void UpdatePositioningFunction()
    {
      if( this.Orientation == Orientation.Vertical )
      {
        this.PositioningFunction = new VerticalPositioning();
        this.PositioningContainerLayoutMode = ContainerLayoutMode.Vertical;
      }
      else
      {
        this.PositioningFunction = new HorizontalPositioning();
        this.PositioningContainerLayoutMode = ContainerLayoutMode.Horizontal;
      }
    }

    private void SetHorizontalOffsetCore( PanelBase panel, double offset )
    {
      offset = Math.Min( offset, panel.ExtentWidth - panel.ViewportWidth );
      offset = Math.Max( 0, offset );

      if( offset != panel.HorizontalOffset )
      {
        if( this.Orientation == Orientation.Horizontal )
        {
          long newValue = ( long )offset;
          long oldValue = ( long )panel.HorizontalOffset;
          long moveDifference = newValue - oldValue;

          if( moveDifference != 0 )
            panel.EnumeratorHelper.Move( moveDifference );
        }

        panel.HorizontalOffset = offset;

        if( panel.ForceInvalidateScrollInfo )
        {
          panel.InvalidateScrollInfo();
        }

        panel.LayoutManager.UpdateViewportContent( new Size( panel.ActualWidth, panel.ActualHeight ) );
      }
    }

    private void SetVerticalOffsetCore( PanelBase panel, double offset )
    {
      // Correct the offset to ensure it is in a valid range for IScrollInfo
      offset = Math.Min( offset, panel.ExtentHeight - panel.ViewportHeight );
      offset = Math.Max( 0, offset );

      if( offset != panel.VerticalOffset )
      {
        if( this.Orientation == Orientation.Vertical )
        {
          long newValue = ( long )offset;
          long oldValue = ( long )panel.VerticalOffset;
          long moveDifference = newValue - oldValue;

          if( moveDifference != 0 )
            panel.EnumeratorHelper.Move( moveDifference );
        }

        panel.VerticalOffset = offset;

        if( panel.ForceInvalidateScrollInfo )
        {
          panel.InvalidateScrollInfo();
        }

        panel.LayoutManager.UpdateViewportContent( panel.RenderSize );
      }
    }

    #endregion

    internal override double GetConfigurationDistanceUsedForExtent( ListBoxContainerConfigurationBase configuration )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        return configuration.PrepareSize.Height;
      }
      else
      {
        return configuration.PrepareSize.Width;
      }
    }


    #region IOrientedView Members

    Orientation IOrientedView.Orientation
    {
      get
      {
        return this.Orientation;
      }
    }

    Orientation IOrientedView.ScrollOrientation
    {
      get
      {
        return this.Orientation;
      }
    }

    #endregion

    #region IOrientedViewManipulator Members
    void IOrientedViewManager.ArrangeElement( PanelBase panel, Size finalSize )
    {

      Point emptyPoint = new Point();

      if( this.Orientation == Orientation.Vertical )
      {
        foreach( ListBoxContainer element in panel.Children )
        {
          double maxHeight = element.Configuration.CachedSize.Height;

          element.Arrange( new Rect( emptyPoint, new Size( finalSize.Width, maxHeight ) ) );
        }
      }
      else
      {
        foreach( ListBoxContainer element in panel.Children )
        {
          double maxWidth = element.Configuration.CachedSize.Width;

          element.Arrange( new Rect( emptyPoint, new Size( maxWidth, finalSize.Height ) ) );
        }
      }
    }

    double IOrientedViewManager.GetAverageContainerSize( PanelBase panel )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        return panel.ViewportSizeInPixels.Height / panel.ItemsInViewport;
      }
      else
      {
        return panel.ViewportSizeInPixels.Width / panel.ItemsInViewport;
      }
    }

    double IOrientedViewManager.ItemsOffset( PanelBase panel )
    {
      return ( ( this.Orientation == Orientation.Vertical ) ?
                  panel.VerticalOffset :
                  panel.HorizontalOffset );

    }

    double IOrientedViewManager.ItemsInViewport( PanelBase panel )
    {
      return ( ( this.Orientation == Orientation.Vertical )
        ? panel.ViewportHeight
        : panel.ViewportWidth );
    }

    void IOrientedViewManager.SetOffset( PanelBase panel, double offset )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        this.SetVerticalOffsetCore( panel, offset );
      }
      else
      {
        this.SetHorizontalOffsetCore( panel, offset );
      }
    }

    void IOrientedViewManager.SetVerticalOffset( PanelBase panel, double offset )
    {
      this.SetVerticalOffsetCore( panel, offset );
    }

    void IOrientedViewManager.SetHorizontalOffset( PanelBase panel, double offset )
    {
      this.SetHorizontalOffsetCore( panel, offset );
    }

    void IOrientedViewManager.ForceItemOffset( PanelBase panel, double offset )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        panel.ForceVerticalOffset( offset );
      }
      else
      {
        panel.ForceHorizontalOffset( offset );
      }
    }

    void IOrientedViewManager.ProcessMouseWheel( PanelBase panel, MouseWheelEventArgs e )
    {
      if( this.Orientation == Orientation.Vertical )
      {
        if( e.Delta < 0 )
        {
          panel.MouseWheelDown();
        }
        else
        {
          panel.MouseWheelUp();
        }
      }
      else
      {
        if( e.Delta < 0 )
        {
          panel.MouseWheelRight();
        }
        else
        {
          panel.MouseWheelLeft();
        }
      }
    }

    #endregion
  }
}
